home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************
-
- File: PathWalking.c
-
-
- Copyright ©, 1996, Apple Computer Inc.
- Daniel I. Lipton
-
- This file contains routines necessary to implement the shape
- walking library. This library can be used for traversing of
- line and curve segments in shapes. The library is callback
- driven. Waling a shape will result in callbacks for each segment.
-
- Moveto callback: changes the current point, called at start of new contour.
- Lineto callback: a line segment from current point to the specified point.
- Curveto callback: a curve segment, specified by the 3 points. The 1st of the three
- will be the current point, but all three are passed anyway.
- Closepath callback: Called for each contour of a closed shape. (gxCloseFrameFill)
-
- each callback gets the refcon passed into the ShapeWalker call.
- This refcon can be used for maintaining state necessary for client's use
- of the shape walking libraary. It is reccomended that the client use a pointer to a
- structure that at least contains and maintains the current point. This current point
- would get updated in each of the callbakds.
-
- The return value from the callbacks is a boolean indicating whether or not to stop early.
- Returning false will result in the PathWalker routine to continue traversing. returning
- true will cause termination early.
-
-
-
- ***************************************************************/
-
-
- #include "Types.h"
- #include "GXTypes.h"
- #include "GXMath.h"
- #include "GXGraphics.h"
- #include "GXLayout.h"
- #include "GXExceptions.h"
-
- #include "PathWalking.h"
-
-
-
- /****************************
- NEXTBIT, PREVBIT:
- Used for tranversal of bit arrays.
-
- theBit: Boolean, true if bit was set.
- theLong: pointer to the current longword in bit array.
- theMask: current long word mask. Initialize to 0x80000000 for first bit in array.
-
- NEXTBIT Checks current bit and then advances mask
- PREVBIT rolls back mask and then checks bit.
-
- *****************************/
- #define NEXTBIT(theBit, theLong, theMask) {\
- theBit = ((*theLong & theMask) != 0); \
- if (!(theMask>>=1)) \
- {theMask = 0x80000000; ++theLong;} \
- }
-
- #define PREVBIT(theBit, theLong, theMask) {\
- if (!(theMask<<1)) \
- {theMask = 0x00000001; --theLong;} \
- theBit = ((*theLong & theMask) != 0); \
- }
-
-
-
- /****************************************
- Macro to interpolate between two points.
-
- p: The point that will get the result.
- p1: The first point to interpolate between
- p2: The second point to interpolate between
-
- *****************************************/
-
- // Code to average two fixed point numbers. Taken from GX file "paths.h" Does math in 33 bits.
- static long Average(register long x, register long y)
- {
- return (x >> 1) + (y >> 1) + ((x & y) & 1);
- }
-
-
- /* Macro to interpolate the point p which will be centered between p1 and p2 */
-
- #define INTERPPOINT(p, p1, p2) {\
- (p).x = Average( (p1).x, (p2).x) ; \
- (p).y = Average( (p1).y, (p2).y) ; \
- }
-
-
-
-
- /***********************************
- PathWalker:
-
- Walk a path shape, call back with each segment.
- The points sent through the callbacks will include
- any implicit points in the path determined by consecutive
- off curve points.
-
- theShape: the path shape to walk.
- DoMoveto: call back function for setting current point.
- DoLineto: call back function for a line segment.
- DoCurveto: call back function for a curve segment.
- DoClosepath: call back function for closing the path.
- refcon: this will get passed to your callbacks.
-
- *************************************/
- Boolean PathWalker(gxShape theShape, TpwMovetoProc DoMoveto, TpwLinetoProc DoLineto, TpwCurvetoProc DoCurveto, TpwClosepathProc DoClosepath, void* refcon);
- Boolean PathWalker(gxShape theShape, TpwMovetoProc DoMoveto, TpwLinetoProc DoLineto, TpwCurvetoProc DoCurveto, TpwClosepathProc DoClosepath, void* refcon)
- {
- Boolean status;
- Ptr inPath; // We must walk through the Paths structure
- gxShapeAttribute theAttributes;
- long i, pointIndex;
- long size;
- long firstIndex, lastIndex; // Index of first and last point ON curve.
- Boolean closeIt; // Is it open or closed.
- long nContours; // Number of contours.
- long nPoints; // Number of points in contour.
- gxPoint* pPoint; // Point to a point.
- gxPoint firstPoint; // First point in the contour.
- gxPoint lastPoint; // The last point we output.
- register unsigned long* controlBitLong; // Pointer to control bits.
- register unsigned long cBitMask; // Mask for reading control bits.
- unsigned long lastBitMask; // Mask for last point in contour.
- long cBitSize; // Number of longs for control bits.
- gxPoint q[3]; // Quadratic control points.
- short qIndex; // Indicates #points in curve segment so far.
- Boolean isOff; // On the curve or off the curve.
- Boolean firstIsOn, lastIsOn;
-
- /* If it is closedFrameFill we must close each contour */
-
- closeIt = (GXGetShapeFill(theShape) == gxClosedFrameFill);
-
- /***********
- Make sure we have access to the shape data structure,
- and then get it
- ***********/
- theAttributes = GXGetShapeAttributes(theShape);
- GXSetShapeAttributes(theShape, theAttributes | gxDirectShape);
-
- GXLockShape(theShape);
-
- inPath = (Ptr)GXGetShapeStructure(theShape, &size); //Get a pointer to the structure.
- check(inPath);
-
- nContours = ((gxPaths*)inPath)->contours; //Get the number of countours.
- inPath += sizeof(long); //Move pointer past contours field.
-
- status = GXGetGraphicsError(nil);
- nrequire(status, failed_KeepDirect);
-
- /** Now output all of the contours, break up to fit on stack if necessary **/
-
- for (i = 0; i < nContours; i++) { // Output the contours.
-
- nPoints = *(long*)inPath; // Get the number of points for this contour.
- inPath += sizeof(long); // Move to the data.
-
- if (nPoints > 0) {
-
- controlBitLong = (unsigned long*)inPath; // Get to the control bits.
-
- cBitSize = (nPoints + 31) / 32; // Compute the size of control bits in longs.
-
- inPath += cBitSize * sizeof(long); // Skip past control bits, inPath now points to first point.
-
- pPoint = (gxPoint*)inPath; // Get the first point in contour, leave inPath pointing here.
-
- cBitMask = 0x80000000U; // Initilize the mask for reading bits.
-
- lastBitMask = 0x80000000U >> ((nPoints-1) & 0x0000001F); // Mod 32
-
-
- /** Compute the first and last point on the curve **/
-
- firstIsOn = ((*controlBitLong & cBitMask) == 0);
- lastIsOn = ( ( *(controlBitLong + cBitSize - 1) & lastBitMask) == 0);
-
- if (firstIsOn && lastIsOn) { // They both are on.
-
- firstPoint = *pPoint;
- firstIndex = 0;
- lastPoint = *(pPoint + nPoints - 1);
- lastIndex = nPoints;
- NEXTBIT(isOff, controlBitLong, cBitMask); // Read bit for 1st point, set up for next.
- ++pPoint;
-
- } else if (firstIsOn) { // first was on, last was off
-
- firstPoint = *pPoint;
- firstIndex = 0;
-
- lastPoint = firstPoint;
-
- lastIndex = nPoints + 1;
- NEXTBIT(isOff, controlBitLong, cBitMask); // Read bit for 1st point, set up for next.
- ++pPoint;
-
- } else if (lastIsOn) { // last was on, first is off
-
- register gxPoint* pLastPoint = pPoint + nPoints - 1;
-
- lastPoint = *pLastPoint;
- firstPoint = lastPoint;
- lastIndex = nPoints;
-
- firstIndex = -1;
-
- } else { // they were both off
-
- register gxPoint* pLastPoint = pPoint + nPoints - 1;
-
- firstIndex = -1;
- lastIndex = nPoints + 1;
-
- INTERPPOINT(lastPoint, *pPoint, *pLastPoint);
-
- firstPoint = lastPoint;
-
- } //end if
-
-
- /*** Output the contour ****/
-
- q[0] = firstPoint;
- qIndex = 0;
- status = (*DoMoveto)(&(q[0]), refcon);
- nrequire(status, failed_Output);
-
- // Loop over all points but the first and last ON the curve.
-
- NEXTBIT(isOff, controlBitLong, cBitMask); // Check the first point in the looping.
- pointIndex = firstIndex + 1;
- while (pointIndex < (lastIndex - 1)) { // I know, should cache lastPointIndex-1
-
- if (!isOff) { // If the next point is on the curve:
-
- if ( qIndex == 0) { // If we have only q0, then do a line and start over.
-
- status = (*DoLineto)(pPoint, refcon);
- nrequire(status, failed_Output);
- q[0] = *pPoint++;
- ++pointIndex;
-
- NEXTBIT(isOff, controlBitLong, cBitMask);
-
- } else { // We must have q0 and q1, do a curve: q0, q1, q2
-
- q[2] = *pPoint++;
- status = (*DoCurveto)(q, refcon);
- nrequire(status, failed_Output);
- q[0] = q[2];
- qIndex = 0;
- ++pointIndex;
- NEXTBIT(isOff, controlBitLong, cBitMask);
-
- }//end if
-
- } else {
-
- if (qIndex == 0) { // If we only have one point so far, this becomes 2nd.
-
- q[1] = *pPoint++;
- qIndex = 1;
- NEXTBIT(isOff, controlBitLong, cBitMask);
- ++pointIndex;
-
- } else { // We had 2 points, interpolate to get 3rd and do a curve
-
- INTERPPOINT(q[2], q[1], *pPoint);
-
- status = (*DoCurveto)( q, refcon);
- nrequire(status, failed_Output);
- qIndex = 0;
- q[0] = q[2];
-
- }//end if
-
- }//end if
-
-
- }//end while
-
- /** Now handle the last point that is ON the curve in the contour **/
-
- if (qIndex == 0) { // If we only had one point so far, Do a line.
-
- status = (*DoLineto)( &lastPoint, refcon);
- nrequire(status, failed_Output);
-
- } else { // Else, we must have had 2 points so far, Do a curve.
-
- q[2] = lastPoint;
- status = (*DoCurveto)(q, refcon);
- nrequire(status, failed_Output);
-
- }//end if
-
- if (closeIt) {
-
- status = (*DoClosepath)(refcon);
- nrequire(status, failed_Output);
-
- }//end if
-
- /** Point into the next contour - inPath was pointing to first point in contour **/
-
- inPath += nPoints * sizeof(gxPoint);
-
- }//end if
-
- }//endo for i
-
- failed_Output:
-
- /** Restore things, though the shape is a copy and we really don't have to. **/
-
- GXSetShapeAttributes(theShape, theAttributes);
- GXUnlockShape(theShape);
-
- failed_KeepDirect:
-
-
- return(status);
-
- }//PathWalker
-
-
-
-
- /***********************************
- PolygonWalker:
-
- Walk a polygon and call back
-
- *************************************/
- OSErr PolygonWalker(gxShape theShape, TpwMovetoProc DoMoveto, TpwLinetoProc DoLineto, TpwClosepathProc DoClosepath, void* refcon);
- OSErr PolygonWalker(gxShape theShape, TpwMovetoProc DoMoveto, TpwLinetoProc DoLineto, TpwClosepathProc DoClosepath, void* refcon)
- {
- Boolean status = false;
- Ptr inPolygon; // We must walk through the polygons structure
- gxShapeAttribute theAttributes;
- long i, j;
- Boolean closeIt; // Is it open or closed.
- long nContours; // Number of contours.
- long nPoints; // Number of points in contour.
- gxPoint* pPoint; // Point to a point.
- long size;
-
-
- /* If it is closedFrameFill we must close each contour */
-
- closeIt = (GXGetShapeFill(theShape) == gxClosedFrameFill);
-
- /***********
- Make sure we have access to the shape data structure,
- and then get it
- ***********/
- theAttributes = GXGetShapeAttributes(theShape);
- GXSetShapeAttributes(theShape, theAttributes | gxDirectShape);
- GXLockShape(theShape);
-
- status = GXGetGraphicsError(nil);
- nrequire(status, failed_KeepDirect);
-
-
- inPolygon = (Ptr)GXGetShapeStructure(theShape, &size); //Get a pointer to the structure.
- check(inPolygon);
-
- nContours = ((gxPolygons*)inPolygon)->contours; //Get the number of countours.
-
- inPolygon += sizeof(long); //Move pointer past contours field.
-
- /** Now output all of the contours **/
-
- for (i = 0; i < nContours; i++) { // Output the contours.
-
- nPoints = *(long*)inPolygon; // Get the number of points for this contour.
-
- inPolygon += sizeof(long); // Move to the data.
-
- if (nPoints > 0) {
-
- pPoint = (gxPoint*)inPolygon; // Get the first point in contour.
- inPolygon += sizeof(gxPoint); // Move past it.
-
- status = (*DoMoveto)( pPoint, refcon); // Do the moveto
- nrequire(status, failed_Output);
-
- for (j = 1; j < nPoints; j++) { // Output the points for the countour.
-
- pPoint = (gxPoint*)inPolygon; // Get the next point.
- inPolygon += sizeof(gxPoint);
-
- status = (*DoLineto)( pPoint, refcon); // Add a line to the PS path.
- nrequire(status, failed_Output);
-
- }//end for j
-
- if (closeIt) {
-
- status = (*DoClosepath)(refcon);
- nrequire(status, failed_Output);
-
- }//end if
-
- }//end for
-
- }//endo for i
-
-
- failed_Output:
- /** Restore the shape to the way we got it. **/
-
- GXSetShapeAttributes(theShape, theAttributes);
- GXUnlockShape(theShape);
-
- failed_KeepDirect:
-
- return(status);
-
- }//PolygonWalker
-
-
-
- /**************************************
-
-
- ShapeWalker:
-
- Main entry point for path/polygon traversal.
-
- return result:
- true if shape walking terminated before completion of entire shape, by one of the callbacks.
-
- **************************************/
- Boolean ShapeWalker(gxShape theShape, TpwMovetoProc DoMoveto, TpwLinetoProc DoLineto, TpwCurvetoProc DoCurveto, TpwClosepathProc DoClosepath, void* refcon)
- {
- Boolean result = true;
- gxShapeType theType = GXGetShapeType(theShape);
- gxCurve theCurve;
- gxLine theLine;
- gxRectangle theRectangle;
- gxPoint thePoint;
-
- switch(theType) {
-
- /* paths and polygons require above routines for walking */
- case gxPathType:
- result = PathWalker(theShape, DoMoveto, DoLineto, DoCurveto, DoClosepath, refcon);
- break;
-
- case gxPolygonType:
- result = PolygonWalker(theShape, DoMoveto, DoLineto, DoClosepath, refcon);
- break;
-
- /* other shape types can easily be handled here by calling callbacks with the data */
- case gxCurveType:
- GXGetCurve(theShape, &theCurve);
- result = DoMoveto(&theCurve.first, refcon);
- if (!result) {
- gxPoint *pts = (gxPoint*)&theCurve; // a curve is the same as an array of three points.
- result = DoCurveto(pts, refcon);
- }//end if
- break;
-
- case gxLineType:
- GXGetLine(theShape, &theLine);
- result = DoMoveto(&theLine.first, refcon);
- if (!result)
- result = DoLineto(&theLine.last, refcon);
- break;
-
- case gxRectangleType:
- /* rectangles are a little more work */
- GXGetRectangle(theShape, &theRectangle);
- thePoint.x = theRectangle.left; thePoint.y = theRectangle.top;
- result = DoMoveto(&thePoint, refcon); // (left, top);
- if (!result) {
- thePoint.x = theRectangle.right; // (right, top);
- result = DoLineto(&thePoint, refcon);
- if (!result) {
- thePoint.y = theRectangle.bottom; // (right, bottom);
- result = DoLineto(&thePoint, refcon);
- if (!result) {
- thePoint.x = theRectangle.left; // (left, bottom);
- result = DoLineto(&thePoint, refcon);
- if (!result) {
- result = DoClosepath(refcon); // close it.
- }//end if
- }//end if
- }//end if
- }//end if
-
- /* other shape types not allowed */
- default:
- GXPostGraphicsError(illegal_type_for_shape);
- break;
-
- }//end switch
-
- return(result);
-
- }//ShapeWalker
-
-
-